package com.wxj.bank.module.utils;

import com.wxj.bank.module.modle.criteria.*;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


/**
 * @author shengtao
 * @Description: mongo查询工具类
 * @date 2019/02/01 16:29
 */
public class MongoQueryUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(MongoQueryUtils.class);

    public static ChildWhere getChildWhere(String field,String value){
        return new ChildWhere(field, DataType.Text,value, CompareType.Equal, JoinType.AND);
    }

    public static ChildWhere getChildWhere(String field,int value){
        return new ChildWhere(field, DataType.Number,String.valueOf(value), CompareType.Equal, JoinType.AND);
    }

    public static ChildWhere getChildWhere(String field,boolean value){
        return new ChildWhere(field, DataType.Boolean,String.valueOf(value), CompareType.Equal, JoinType.AND);
    }
    public static ChildWhere getChildWhere(String field,String value,CompareType compareType){
        return new ChildWhere(field, DataType.Text,value, compareType, JoinType.AND);
    }

    public static ChildWhere getChildWhere(String field,int value,CompareType compareType){
        return new ChildWhere(field, DataType.Number,String.valueOf(value),compareType, JoinType.AND);
    }

    public static ChildWhere getChildWhere(String field,boolean value,CompareType compareType){
        return new ChildWhere(field, DataType.Boolean,String.valueOf(value),compareType, JoinType.AND);
    }

    public static ChildWhere getChildWhere(String field,DataType dataType,String value,CompareType compareType,JoinType joinType){
        return new ChildWhere(field, dataType,value,compareType,joinType);
    }

    public static Query getQuery(Where[] wheres){
        Query query = null;
        //条件
        if(wheres != null && wheres.length > 0){
            query = new Query();
            Criteria criteria = getCriteria(wheres);
            query.addCriteria(criteria);
        }
        return query;
    }

    public static Query getQuery(Where[] wheres,OrderBy[] orders,int skip,int limit){
        Query query = null;
        //条件
        if(wheres != null && wheres.length > 0){
            query = new Query();
            Criteria criteria = getCriteria(wheres);
            query.addCriteria(criteria);
        }
        //排序
        if(orders != null && orders.length > 0){
            if(query == null){
                query = new Query();
            }
            List<Sort.Order> orders1 = getOrders(orders);
        }

        if(skip <= 0){
            if(query == null){
                query = new Query();
            }
            query.skip(skip);
        }

        if(limit > 0){
            if(query == null){
                query = new Query();
            }
            query.limit(limit);
        }

        return query;
    }

    /**
     * 构造排序条件
     * @param orders
     * @return
     */
    private static List<Sort.Order> getOrders(OrderBy[] orders) {
        List<Sort.Order> orderss = new ArrayList<>();
        Sort.Direction direction = null;
        for (OrderBy orderBy:orders){
            if(orderBy.isAsc()){
                direction = Sort.Direction.ASC;
            }else {
                direction = Sort.Direction.DESC;
            }
            orderss.add(new Sort.Order(direction,orderBy.getField()));
        }
        return orderss;
    }

    /**
     * 构造查询条件
     * @param wheres
     * @return
     */
    private static Criteria getCriteria(Where[] wheres) {
        Criteria criteria = new Criteria();

        List<Criteria> andCriteriaList = new ArrayList<>();
        List<Criteria> orCriteriaList = new ArrayList<>();
        List<Criteria> noCriteriaList= new ArrayList<>();

        for (int i = 0;i < wheres.length; i++){
            ChildWhere[] childWheres = wheres[i].getChildWheres();
            if(childWheres != null && childWheres.length > 0){
                Criteria whereCriteria = new Criteria();

                List<Criteria> andCriterias = new ArrayList();

                List<Criteria> orCriterias = new ArrayList();

                List<Criteria> norCriterias = new ArrayList();

                Criteria cr = null;
                ChildWhere childWhere = null;

                for (int j = 0;i < childWheres.length;j++){
                    childWhere = childWheres[j];
                    cr = getChildWhere(childWhere);

                    if (cr != null) {
                        switch (childWhere.getJoinType()){
                            case AND:
                                andCriterias.add(cr);
                                break;
                            case OR:
                                orCriterias.add(cr);
                                break;
                            case NOR:
                                norCriterias.add(cr);
                                break;
                        }
                    }
                }

                if(CollectionUtils.isNotEmpty(andCriterias)){
                    whereCriteria.andOperator(andCriterias.toArray(new Criteria[0]));
                }

                if(CollectionUtils.isNotEmpty(orCriterias)){
                    whereCriteria.orOperator(orCriterias.toArray(new Criteria[0]));
                }

                if(CollectionUtils.isNotEmpty(norCriterias)){
                    whereCriteria.norOperator(norCriterias.toArray(new Criteria[0]));
                }

                switch (wheres[i].getJoinType()){
                    case AND:
                        andCriteriaList.add(whereCriteria);
                        break;
                    case OR:
                        orCriteriaList.add(whereCriteria);
                        break;
                    case NOR:
                        noCriteriaList.add(whereCriteria);
                }
            }
        }
        if (andCriteriaList.size() > 0) {
            criteria.andOperator((Criteria[]) andCriteriaList.toArray(new Criteria[0]));
        }

        if (orCriteriaList.size() > 0) {
            criteria.orOperator((Criteria[]) orCriteriaList.toArray(new Criteria[0]));
        }

        if (noCriteriaList.size() > 0) {
            criteria.norOperator((Criteria[]) noCriteriaList.toArray(new Criteria[0]));
        }
        return criteria;
    }

    private static Criteria getChildWhere(ChildWhere childWhere) {

        Criteria cr = null;

        switch (childWhere.getCompareType()){
            case Equal:
                cr = Criteria.where(childWhere.getField()).is(getFieldValue(childWhere));
                break;
            case NoEqual:
                cr = Criteria.where(childWhere.getField()).ne(getFieldValue(childWhere));
                break;
            case GreaterEqual:
                cr = Criteria.where(childWhere.getField()).gte(getFieldValue(childWhere));
                break;
            case LessEqual:
                cr = Criteria.where(childWhere.getField()).lte(getFieldValue(childWhere));
                break;
            case Greater:
                cr = Criteria.where(childWhere.getField()).gt(getFieldValue(childWhere));
                break;
            case Less:
                cr = Criteria.where(childWhere.getField()).lt(getFieldValue(childWhere));
                break;
            default:
                break;
        }
        return cr;
    }

    private static Object getFieldValue(ChildWhere childWhere) {
        Object obj = null;
        switch (childWhere.getDataType()){
            case Text:
                obj = childWhere.getFieldValue();
                break;
            case Number:
                obj = Integer.valueOf(childWhere.getFieldValue());
                break;
            case Boolean:
                obj = Boolean.valueOf(childWhere.getFieldValue());
                break;
            case DateTime:
                try {
                    obj = DateUtils.stringToDate(childWhere.getFieldValue(),DateUtils.DEFAULT_DATEDETAIL_PATTERN);
                }catch (Exception e) {
                    LOGGER.error("解析日期失败 ，ChildWhere：{}", childWhere);
                }
                break;
            default:
                return null;
        }
        return obj;
    }
}
